from model.model import *


class MeModel(Model):
    cameotag_in_hex = '43414d454f544147'  # 'CAMEOTAG'
    non_backdoor_cmds = 'non_backdoor_cmds.csv'
    cmds_tail = '_cmds.csv'
    iss_exe = 'ISS.exe'
    release_note_tail = '_FW_Release Note.txt'
    makefile_commands = {
        'MODEL': '${MODEL_NAME}',
        'ROOT': '${MGM_PROJECT_BASE_DIR}',
        'DISPLAY_MODEL_NAME': '${DISPLAY_MODEL_NAME}',
        'BOOT_REPO': '$(MGM_PROJECT_BASE_DIR)/boot/$(BOOT_PACKAGE)',
        'OS_REPO': '$(MGM_PROJECT_BASE_DIR)/os/$(OS_PACKAGE)',
        'SDK_REPO': '$(MGM_PROJECT_BASE_DIR)/sdk/$(SDK_PACKAGE)',
        'CORE_REPO': '$(MGM_PROJECT_BASE_DIR)/core/code',
        'CUSTOMER_REPO': '${CUSTOMER_ABSOLUTE_DIR}',
        'ISS_HW_VERSION': '${ISS_HW_VERSION}',
        'ISS_SW_VERSION': '${ISS_SW_VERSION}',
        'OUTPUT_IMAGE_DIR': '${IMAGE_ABSOLUTE_DIR}/${OUTPUT_IMAGE_DIR}',
        'OUTPUT_IMAGE': '${SHOW_MODEL}ME-${ISS_HW_VERSION}-${ISS_SW_VERSION}.img',
        'OUTPUT_ZOS2_IMAGE': '${SHOW_MODEL}ME-${ISS_HW_VERSION}-${ISS_SW_VERSION}.zos2',
        'OUTPUT_RFS2_IMAGE': '${SHOW_MODEL}ME-${ISS_HW_VERSION}-${ISS_SW_VERSION}.rfs2',
        'OUTPUT_BIN': '${SHOW_MODEL}ME-${ISS_HW_VERSION}-${ISS_SW_VERSION}.bin',
        'OUTPUT_HEX': '${SHOW_MODEL}ME-${ISS_HW_VERSION}-${ISS_SW_VERSION}.hex',
        'OUTPUT_BOOT_HEX': '${SHOW_MODEL}ME-${ISS_HW_VERSION}-${ISS_SW_VERSION}-BOOT.hex',
        'OUTPUT_OS_HEX': '${SHOW_MODEL}ME-${ISS_HW_VERSION}-${ISS_SW_VERSION}-OS.hex',
        'OUTPUT_FS_HEX': '${SHOW_MODEL}ME-${ISS_HW_VERSION}-${ISS_SW_VERSION}-FS.hex',
        'OUTPUT_ALL_HEX': '${SHOW_MODEL}ME-${ISS_HW_VERSION}-${ISS_SW_VERSION}-ALL.hex',
        'OUTPUT_STANDARD_MIB': 'STANDARD.mib',
        'OUTPUT_SNMP_MIB': 'STANDARD-SNMP.mib',
        'OUTPUT_PRIVATE_MIB': '${SHOW_MODEL}ME-${MIB_HW_VERSION}-${MIB_SW_VERSION}.mib',
        'OUTPUT_BULK_BIN': '${COMMON_BOARD_NAME}_${SHOW_MODEL}_${ISS_SW_VERSION}.img',
    }

    def __init__(self, **kwargs):
        super(MeModel, self).__init__(**kwargs)
        self._support_make_type = (make_all_clean_core_id, make_core_id, make_all_id, make_mib_id)
        self._make_dict.update({
            make_mib_id: self._make_mib,
        })

        self._checksum_runtime = ''
        self._checksum_prom = ''

        self._ftp_var = {
            'hex': '', 'all_hex': '',
            'backup': '', 'last_release_note': ''}
        self._ftp_last_release_max_search_cnt = 5

        self._local_hex_path = ''
        self._local_hex_all_path = ''
        self._local_backup_path = ''
        self._project_name = 'me'
        self._release_note_tail_str = self.release_note_tail

    def _make_mib(self) -> Popen:
        args = 'make private-mib'
        return self._make(args)

    def update_all_repos(self):
        super(MeModel, self).update_all_repos()
        RGit(self._release_var['OS_REPO']).update()
        RGit(self._release_var['SDK_REPO']).update()
        RGit(self._release_var['CORE_REPO']).update()
        RGit(self._release_var['CUSTOMER_REPO']).update()
        RGit(self._release_var['BOOT_REPO']).update()
        io.emit(edm_model_info_report, f'Model {self.__str__()} repos updated.')

    def _sync_local_dirs(self):
        super(MeModel, self)._sync_local_dirs()
        model_release_to = combine_path([
            cfg.local_rls_store.__str__(),
            self.__str__(),
            self._iss_sw_ver().split('-')[-1],
        ])
        local_hex_path = Path(combine_path([
            model_release_to,
            'hex'
        ]))
        local_all_hex_path = Path(combine_path([
            model_release_to,
            'all_hex'
        ]))
        local_backup_path = Path(combine_path([
            model_release_to,
            'backup'
        ]))
        self._local_hex_path = local_hex_path.__str__()
        self._local_hex_all_path = local_all_hex_path.__str__()
        self._local_backup_path = local_backup_path.__str__()

    def _sync_ftp_dirs(self):
        super(MeModel, self)._sync_ftp_dirs()
        self._ftp_var['hex'] = self._ftp_model_dir(cfg.release_ftp_fw_dir_list_me[0])
        self._ftp_var['all_hex'] = self._ftp_model_all_dir()
        self._ftp_var['backup'] = self._ftp_backup_dir()
        io.emit(edm_model_info_report, f'Model {self.__str__()} ftp path synchronized')
        # TODO: Uncomment this for production env
        # !!!NOTE!!!: DO NOT use this method for get old release note, it may be removed in further version.
        # self._ftp_var['last_release_note'] = self._ftp_last_release_note_dir()

    def _sync_file_info(self):
        super(MeModel, self)._sync_file_info()
        """
        the local-remote map of release files

        :return: dict type file info
        """

        if self._make_type != make_mib_id:
            print('make type is not mib, update all files info')
            file_info = [{
                'name': self.__str__(),
                'children':
                    [
                        {
                            'name': 'Hex files',
                            'children': [
                                {
                                    'name': self._release_var['OUTPUT_HEX'],
                                    'local': self._local_hex_path,
                                    'ftp': self._ftp_hex_path(),
                                },
                                {
                                    'name': self._hex_prefix() + self.cmds_tail,
                                    'local': self._local_hex_path,
                                    'ftp': self._ftp_hex_path()
                                },
                                {
                                    'name': self._hex_prefix() + self.release_note_tail,
                                    'local': self._local_hex_path,
                                    'ftp': self._ftp_hex_path()
                                }
                            ]
                        },
                        {
                            'name': 'All hex files',
                            'children': [
                                {
                                    'name': self._release_var['OUTPUT_ALL_HEX'],
                                    'local': self._local_hex_all_path,
                                    'ftp': self._ftp_all_hex_path(),
                                },
                            ]
                        },
                        {
                            'name': 'Backup files',
                            'children': [
                                {
                                    'name': self._release_var['OUTPUT_BIN'],
                                    'local': self._local_backup_path,
                                    'ftp': self._ftp_backup_path(),
                                },
                                {
                                    'name': self.iss_exe,
                                    'local': self._local_backup_path,
                                    'ftp': self._ftp_backup_path()
                                }
                            ]
                        }
                    ]
            }]
        else:
            file_info = [{
                'name': self.__str__(),
                'children':
                    [
                        {
                            'name': 'Hex files',
                            'children': [
                                {
                                    'name': self._release_var['OUTPUT_PRIVATE_MIB'],
                                    'local': self._local_hex_path,
                                    'ftp': self._ftp_hex_path()
                                },
                            ]
                        }
                    ]
            }]

        self._file_info = file_info

    def _release_to_local(self):
        super(MeModel, self)._release_to_local()

        local_release_note = combine_path([
            self._local_hex_path.__str__(),
            self._hex_prefix() + self.release_note_tail
        ])
        Path(self._local_hex_path).mkdir(parents=True, exist_ok=True)
        Path(self._local_hex_all_path).mkdir(parents=True, exist_ok=True)
        Path(self._local_backup_path).mkdir(parents=True, exist_ok=True)
        if self._make_type != make_mib_id:
            shutil.copy(self._hex(), self._local_hex_path)
            shutil.copy(self._cmds(), combine_path([
                self._local_hex_path.__str__(),
                self._hex_prefix() + self.cmds_tail
            ]))
            shutil.copy(self._all_hex(), self._local_hex_all_path)
            shutil.copy(self._bin(), self._local_backup_path)
            shutil.copy(self._iss(), self._local_backup_path)
            with open(local_release_note, 'w') as f:
                f.writelines(self._gen_release_note())
        shutil.copy(self._mib(), self._local_hex_path)

        self._local_released_flag = True

    # def upload_all_to_ftp(self):
    #     super(MeModel, self).upload_all_to_ftp()
    #     ftp = RFtp(cfg.ftp_host)
    #     ftp.login(cfg.ftp_user, cfg.ftp_passwd)
    #
    #     for i in Path(self._local_hex_path).iterdir():
    #         print(str(i).split('/')[-1])
    #         nm = str(i).split('/')[-1]
    #         try:
    #             ftp.upload_file(self._local_hex_path, self._ftp_hex_path(), nm)
    #         except:
    #             print('%s upload failed.\n' % (self._local_hex_path))
    #     for i in Path(self._local_hex_all_path).iterdir():
    #         print(str(i).split('/')[-1])
    #         nm = str(i).split('/')[-1]
    #         try:
    #             ftp.upload_file(self._local_hex_all_path, self._ftp_all_hex_path(), nm)
    #         except:
    #             print('%s upload failed.\n' % (self._local_hex_all_path))
    #     for i in Path(self._local_backup_path).iterdir():
    #         print(str(i).split('/')[-1])
    #         nm = str(i).split('/')[-1]
    #         try:
    #             ftp.upload_file(self._local_backup_path, self._ftp_backup_path(), nm)
    #         except:
    #             print('%s upload failed.\n' % (self._local_backup_path))
    #
    #     ftp.close()

    def _ftp_hex_path(self):
        return self._ftp_var['hex']

    def _ftp_all_hex_path(self):
        return self._ftp_var['all_hex']

    def _ftp_backup_path(self):
        return self._ftp_var['backup']

    def _ftp_last_release_path(self) -> str:
        return self._ftp_var['last_release_note']

    def _gen_checksum(self):
        # extract prom and runtime checksum from hex and boot hex
        hex_file = combine_path(
            [self._release_var['OUTPUT_IMAGE_DIR'], self._release_var['OUTPUT_HEX']])
        boot_hex = combine_path([
            self._release_var['OUTPUT_IMAGE_DIR'],
            self._release_var['OUTPUT_BOOT_HEX']
        ])
        cameo_tag = self.cameotag_in_hex
        # runtime checksum
        with open(str(hex_file), 'rb') as bf:
            str_out = binascii.b2a_hex(bf.read()).decode()
            goto = str_out.find(cameo_tag) + 32
            str_out_left = str_out[goto:]
            cameo_tag_start = str_out_left.find(cameo_tag) + len(cameo_tag) + 8
            cameo_tag_end = cameo_tag_start + 8
            runtime_checksum = str_out_left[cameo_tag_start:cameo_tag_end]
            self._checksum_runtime = '0x' + runtime_checksum
        # prom_checksum
        with open(str(boot_hex), 'rb') as bf:
            str_out = binascii.b2a_hex(bf.read()).decode()
            cameo_tag_start = \
                str_out.find(cameo_tag) + len(cameo_tag) + 8
            cameo_tag_end = cameo_tag_start + 8
            prom_checksum = str_out[cameo_tag_start:cameo_tag_end]
            self._checksum_prom = '0x' + prom_checksum

    def _gen_release_note(self) -> []:
        self._gen_checksum()
        self._gen_git_info()
        last_release_note_path = self._ftp_last_release_path()

        if self._last_release_note:
            head = self._last_release_note[0:10]
            history = self._last_release_note[10:]
        elif last_release_note_path and len(last_release_note_path) > 0:
            ftp = RFtp(cfg.ftp_host)
            ftp.login(cfg.ftp_user, cfg.ftp_passwd)
            dir_name = os.path.dirname(last_release_note_path)
            nm = last_release_note_path.split('/')[-1]
            buf = bio.BytesIO()
            ftp.download_file_to_buf(buf, dir_name, nm)
            buf.seek(0)
            content = [i.decode() for i in buf.readlines()]
            ftp.close()

            head = content[0:10]
            history = content[10:]
        else:
            head = history = []
        return head + self._release_note() + history

    def _release_note(self) -> []:
        text = []
        today = datetime.date.today()
        text.append(
            '________________________________________________________________________________\r\n')
        text.append(
            '        RELEASE VERSION  : %s\r\n' % (
                self._iss_sw_ver().replace('-', '.')))
        text.append('        RELEASE DATE     : %04d/%02d/%02d\r\n' % (
            today.year, today.month, today.day))
        text.append(
            '        RELEASEE BY      : %s\r\n' % (cfg.releaser))
        text.append(
            '________________________________________________________________________________\r\n\r\n')

        text.append('[CHECK-CODE]    : \r\n')
        text.append('[CHECKSUM]      : [N/A]\r\n')
        text.append('[PART-NO]       : [N/A]\r\n')
        text.append('[GIT]           :\r\n%s\r\n\r\n' % self._git_info)

        text.append('[TMPL-VER]      : [N/A]\r\n')
        text.append('[ECN-NO]        : [N/A]\r\n')
        text.append('[TP-TIME]       : [N/A]\r\n')
        text.append('[FW-VER]        : %s\r\n' % (
            self._iss_sw_ver().replace('-', '.')))
        text.append('[PLM-NO]        : [N/A]\r\n')
        text.append(
            '________________________________________________________________________________\r\n\r\n')

        text.append('New Feature:\r\n')
        text.append('-------------\r\n')
        text.append('%s\r\n\r\n' % (self._new_feature.replace('\n', '\r\n')))

        text.append('Bug Fixed:\r\n')
        text.append('-------------\r\n')
        text.append('%s\r\n\r\n' % (self._bug_fixed.replace('\n', '\r\n')))

        text.append('Changed:\r\n')
        text.append('-------------\r\n')
        text.append('%s\r\n\r\n' % (self._changed.replace('\n', '\r\n')))

        text.append('Note:\r\n')
        text.append('%s\r\n\r\n' % (self._note.replace('\n', '\r\n')))

        text.append('Version :\r\n')
        text.append('    HW      : %s\r\n' % (self._iss_hw_ver()))
        text.append('    PROM    : 1.01.033\r\n')
        text.append('    Runtime : %s\r\n\r\n' % (
            self._iss_sw_ver().replace('-', '.')))

        text.append('Checksum :\r\n')
        text.append('    PROM    : %s\r\n' % self._checksum_prom)
        text.append('    Runtime : %s\r\n\r\n' % self._checksum_runtime)

        text.append('Known issue:\r\n')
        text.append('%s\r\n\r\n' % (self._known_issue.replace('\n', '\r\n')))
        text.append('-------------\r\n\r\n')
        return text

    """ NOTE: All these methods for guessing ftp path are NOT accurate """

    def _iss_hw_ver(self) -> str:
        return self._release_var['ISS_HW_VERSION']

    def _iss_sw_ver(self) -> str:
        return self._release_var['ISS_SW_VERSION']

    def _hex_prefix(self) -> str:
        return self._release_var['OUTPUT_HEX'].split('.')[0]

    def _hex(self):
        return combine_path([
            self._release_var['OUTPUT_IMAGE_DIR'],
            self._release_var['OUTPUT_HEX']
        ])

    def _mib(self):
        return combine_path([
            self._release_var['OUTPUT_IMAGE_DIR'],
            self._release_var['OUTPUT_PRIVATE_MIB']
        ])

    def _all_hex(self):
        return combine_path([
            self._release_var['OUTPUT_IMAGE_DIR'],
            self._release_var['OUTPUT_ALL_HEX']
        ])

    def _cmds(self):
        return combine_path([
            self._release_var['OUTPUT_IMAGE_DIR'],
            self.non_backdoor_cmds
        ])

    def _bin(self):
        return combine_path([
            self._release_var['OUTPUT_IMAGE_DIR'],
            self._release_var['OUTPUT_BIN'],
        ])

    def _iss(self):
        return combine_path([
            self._release_var['OUTPUT_IMAGE_DIR'],
            self.iss_exe
        ])

    def _version_x_dot_xx(self) -> str:
        a = self._release_var['ISS_SW_VERSION'].split('-')
        return '.'.join(a[:len(a) - 1])

    def _version_x0xx(self) -> str:
        return self._release_var['ISS_SW_VERSION'].split('-')[-1]

    @staticmethod
    def ver_2_last_ver(ver: str) -> str:
        g = re.match(r'([a-zA-Z]*[0]*)([0-9]*)', ver)
        x0xx = g.group(1) + (
                '%*s' % (len(g.group(2)), int(g.group(2)) - 1)).replace(' ',
                                                                        '0')
        return x0xx

    def _ftp_model_name(self, ver_x_dot_xx: str = None) -> str:
        if ver_x_dot_xx is None:
            ver_x_dot_xx = self._version_x_dot_xx()
        d_n = self._display_model_name()
        h_v = self._iss_hw_ver()
        if ver_x_dot_xx == '20.04':
            """ TODO: 52-C1 """
            ...
        elif ver_x_dot_xx in ('10.05', '6.11'):
            return d_n + '-ME_' + h_v
        elif ver_x_dot_xx == '6.14':
            return d_n + '-ME'
        elif ver_x_dot_xx == '7.03' and 'X' not in d_n and 'TS' not in d_n:
            return d_n + '-ME-' + h_v
        elif ver_x_dot_xx == '7.03' and ('X' in d_n or 'TS' in d_n):
            return d_n + '-ME'

    def _ftp_last_release_note_dir(self) -> str:
        loop_cnt = 0
        x0xx = self._version_x0xx()
        ftp = RFtp(cfg.ftp_host)
        ftp.login(cfg.ftp_user, cfg.ftp_passwd)
        while True:
            loop_cnt += 1
            if loop_cnt > self._ftp_last_release_max_search_cnt:
                return ''
            x0xx = self.ver_2_last_ver(x0xx)
            for d in cfg.release_ftp_fw_dir_list_me:
                ftp_last_release_note = combine_path([
                    self._ftp_model_dir(d, x0xx=x0xx),
                    self._hex_prefix().replace(
                        self._version_x0xx(),
                        x0xx
                    ) + self.release_note_tail
                ])
                if ftp.is_file_exists(ftp_last_release_note):
                    ftp.close()
                    return ftp_last_release_note

    def _ftp_model_dir(self, fw_dir: str, x0xx: str = None) -> str:
        """ TODO: may not be accurate """
        if x0xx is None:
            x0xx = self._version_x0xx()
        dmn_a = self._display_model_name().split('-')
        dmn = '-'.join(dmn_a[:len(dmn_a) - 1])
        me_token = '_ME_'
        v_a = self._iss_sw_ver().split('-')
        v_token = 'v' + '.'.join(v_a[0:len(v_a) - 1]) + '.' + x0xx[0] + 'xxx'
        if self._display_model_name() == 'DES-1210-52':
            """ TODO: DES-1210-52 is special """
            base_dir = 'DES-1210-52ME_' + v_token
            return combine_path([
                fw_dir,
                x0xx,
                base_dir
            ])
        else:
            base_dir = dmn + me_token + v_token
            return combine_path([
                fw_dir, x0xx,
                base_dir, self._ftp_model_name()
            ])

    def _ftp_backup_dir(self) -> str:
        """ TODO: may not be accurate """
        if self._display_model_name() == 'DES-1210-52':
            """ TODO: DES-1210-52 is special """
            ...
        dmn_a = self._display_model_name().split('-')
        dmn = '-'.join(dmn_a[:len(dmn_a) - 1])
        me_token = '_ME_'
        v_a = self._iss_sw_ver().split('-')
        v_token = 'v' + '.'.join(v_a[0:len(v_a) - 1]) + '.' + v_a[-1][0] + 'xxx'
        if self._display_model_name() == 'DES-1210-52':
            """ TODO: DES-1210-52 is special """
            base_dir = 'DES-1210-52ME_' + v_token
            return combine_path([
                cfg.release_ftp_fw_backup_dir_list_me[0],
                self._iss_sw_ver().split('-')[-1],
                base_dir
            ])
        else:
            base_dir = dmn + me_token + v_token
            return combine_path([
                cfg.release_ftp_fw_backup_dir_list_me[0], self._iss_sw_ver().split('-')[-1],
                base_dir, self._ftp_model_name()
            ])

    def _ftp_model_all_dir(self) -> str:
        """ TODO: may not be accurate """
        dxs = self._display_model_name().split('-')[0]
        v_a = self._iss_sw_ver().split('-')
        v_token = '-'.join(v_a[0:len(v_a) - 1]) + '-' + v_a[-1][0]
        if dxs == 'DGS' and v_token == '6-14-E':
            v_token += 'xxx'
        else:
            v_token += 'xx'
        base_dir = dxs + '-' + v_token
        return combine_path(
            [cfg.release_ftp_fw_dir_list_me[0], self._iss_sw_ver().split('-')[-1], 'AllHEX',
             base_dir])
